--------------
---hivemind Copyright 2020 Francisco "FreeLikeGNU" Athens, MIT license
--------------
-- hivemind enables player-entity and entity-entity relations to be transfered between entities
-- so that we have a more natural flow of relationships and meaningful interactions throughout a worldspace.

local function strip_escapes(input)
  hivemind.strip_escapes(input)
end

local function print_s(input)
  print(hivemind.strip_escapes(input))
end

local S = minetest.get_translator("hivemind")

---hivemind.encounter can be used to initialize, query and write data in the form of key/(int)value pairs
-- to an entity as self.hivemind
--@ the optional "relation" is a {key = (int)value} pair to which we will write, queries should not include this
function hivemind.encounter (self, target_name, relation)
  local hm_t = target_name
  local hm_r = relation
  local ent = ""
  --are we a player?
  if hm_t and hm_t:is_player() then
    ent = hm_t:get_player_name()
  else
  ---figure something else out
  end
  --haz hivemind?
  if not self.hivemind then --give us a brain
    self.hivemind = {id = "hm_"..string.format("%x", math.random()*10^14)} --create our ID
    --create our own dataset
    self.hivemind[self.hivemind.id] = {}
  end

  local shm = self.hivemind -- either existing or freshly created brains!
  if not hm_t then return shm end --we may be done if we are just querying everthing we know
  --we must have a target if we got this far
  if not shm[shm.id][ent] then --if no local entry exists we need to identify and create an entry for the target
    shm[shm.id][ent] = {fe = os.time()} --"fe" stands for "first encounter"
  end

  if hm_t and not hm_r then -- we just have a query about the player (or mob)
    --we may have just met a player, create a table for it
    if not shm[shm.id][ent] then shm[shm.id][ent] = {} end
    return shm[shm.id][ent] -- return what we know about the target
  end

  if hm_t and hm_r then
    for k,v in pairs(hm_r) do -- write the tables
      if not shm[shm.id][ent][k] then shm[shm.id][ent][k] = 0 end --initialize
      shm[shm.id][ent][k] = shm[shm.id][ent][k] + v
      shm[shm.id][ent]["le"] = os.time() --"le" stands for "last encounter"
    end
    return shm[shm.id][ent][hm_r]
  end
end

---hivemind.propogate transfers knowledge between entities
-- we scan for others in the collective in range every 4-6 sec unless defined otherwise
function hivemind.pull(self,t_min,t_max,range)
  local t_min = t_min or 4
  local t_max = t_max or t_min + 2
  if self.last_time and self.last_time + math.random(t_min, t_max) > os.time() then return end
  self.last_time = os.time()
  --local ent = target:get_luaentity()
  local pos = vector.round(self.object:getpos())
  local s = self.object:get_pos()
  local objs = minetest.get_objects_inside_radius(s, range or self.view_range or 10)
  --print_s(S(dump(aggro_wielded)))
  -- remove entities we aren't interested in
  for n = 1, #objs do
    local ent = objs[n]:get_luaentity()
    -- are we a player?
    if objs[n]:is_player() then --we don't care now
    -- local pname = objs[n]:get_player_name()
    --if not ent.hivemind then ent.hivemind["id"] = math.random() end
    elseif ent == self then
    --we dont care about our own brain!
    elseif not ent.hivemind then
    ---they have nothing to say!
    else
      print("I found a brain: "..dump(ent.hivemind.id))
      local  mst = minetest.serialize
      local  mdt = minetest.deserialize
      local ehm = ent.hivemind
      local ehm_s = mst(ehm)
      --round up our own relations (as we may not even have any!)
      local shm = hivemind.encounter(self)
      --get the knowledge
      print("Hi, I am: "..dump(shm.id))
      for k,v in pairs(ehm) do
        if k == "id"  then -- record our meeting
          if not shm[shm.id][ehm.id] then
            shm[shm.id][ehm.id] = {fe = os.time()}
            print(" we are meeting for the first time!")--make an introduction
            shm[shm.id][ehm.id].le = os.time()
        end
        shm[shm.id][ehm.id].le = os.time()
        --print("( "..shm.id.." meeting "..ehm.id.. " recorded at "..shm[shm.id][ehm.id].le.." )")
        elseif k ~= shm.id then
          print(" We are talking about ".. k.."'s encounters")
          for x,y in pairs(ehm[k]) do
            if x ~= shm.id then
              print("  ehm: "..x.." "..mst(y))
              if not shm[k]then 
                print("  I didn't know anything about " ..k)
                shm[k] = {}
              end
              if not shm[k][x]  then--we need to fill the gap
                print("  ehm le = "..ehm[k][x].le.. " vs " ..mst(shm[k]))
                shm[k][x] = table.copy(ehm[k][x])
                print("  I just learned about"..mst(shm[k]))
              end
              if shm[k][x] and shm[k][x].le then
                print(  "   shm knows about : "..k.."  "..mst(shm[k]))
                if shm[k][x].le < ehm[k][x].le then
                  print("    ...but I did not know that about " ..x.." and "..k)
                  shm[k][x] = table.copy(ehm[k][x])
                  print("     and now I know what " ..x.." did since " ..shm[k][x].le)
                end
              end
            end
          end
        end
      end
    end
  end
end


